// rom audit dialog module
#include "burner.h"

bool avOk = false;
bool rescanRoms = false;

static unsigned scanThreadId = 0;
static HANDLE hScanThread = NULL;
static bool checkOnce = false;

HWND hRomsDlg = NULL;
static HANDLE hEvent = NULL;

static unsigned romscanMaxRange = 0;

static void createRomDatName(wchar_t* romDat)
{
	swprintf(romDat, L"config\\%s.roms.dat", appExeName);
	return;
}

///////////////////////////////////////////////////////////////////////////////
//Check Romsets////////////////////////////////////////////////////////////////

static int writeGameAvb()
{
	wchar_t romDat[MAX_PATH];
	createRomDatName(romDat);

	FILE* file = _wfopen(romDat, L"wt");
	if (file == NULL) {
		return 1;
	}

	unsigned drvCount = BurnGetDrvCount();

	fwprintf(file, APP_TITLE L" v%.20s ROMs", appBurnVer);	// identifier
	fwprintf(file, L" 0x%04X ", drvCount);					// no of games

	for (unsigned int i = 0; i < drvCount; i++) {
		if (auditState->get(i) == AuditState::AUDIT_FULLPASS) {
			fputwc('*', file);
		}
		else if (auditState->get(i) == AuditState::AUDIT_PARTPASS) {
			fputwc('+', file);
		}
		else {
			fputwc('-', file);
		}
	}

	fwprintf(file, L" END");									// end marker

	fclose(file);
	return 0;
}

static int doCheck(wchar_t* buffPos)
{
	wchar_t label[256];

	// Check identifier
	memset(label, 0, sizeof(label));
	swprintf(label, APP_TITLE L" v%.20s ROMs", appBurnVer);
	if ((buffPos = labelCheck(buffPos, label)) == NULL) {
		return 1;
	}

	unsigned drvCount = BurnGetDrvCount();

	// Check no of supported games
	memset(label, 0, sizeof(label));
	memcpy(label, buffPos, 16);
	buffPos += 8;
	unsigned int n = wcstol(label, NULL, 0);
	if (n != drvCount) {
		return 1;
	}

	if (checkOnce) {
		avOk = true;
		return 0;
	}

	for (unsigned int i = 0; i < drvCount; i++) {
		if (*buffPos == '*') {
			auditState->set(i, AuditState::AUDIT_FULLPASS);
		}
		else if (*buffPos == '+') {
			auditState->set(i, AuditState::AUDIT_PARTPASS);
		}
		else if (*buffPos == '-') {
			auditState->set(i, AuditState::AUDIT_FAIL);
		}
		else {
			return 1;
		}

		buffPos++;
	}

	memset(label, 0, sizeof(label));
	swprintf(label, L" END");
	if (labelCheck(buffPos, label) == NULL) {
		avOk = true;
		checkOnce = true;
		return 0;
	}

	return 1;
}

static int checkGameAvb()
{
	unsigned int bufferSize = BurnGetDrvCount() + 256;
	wchar_t* buffer = new wchar_t[bufferSize];
	if (buffer == NULL) {
		return 1;
	}
	memset(buffer, 0, bufferSize * sizeof(wchar_t));

	wchar_t romDat[MAX_PATH];
	createRomDatName(romDat);

	FILE* file = _wfopen(romDat, L"r");
	if (file == NULL) {
		free(buffer);
		return 1;
	}

	fgetws(buffer, bufferSize, file);
	fclose(file);

	int ret = doCheck(buffer);

	delete [] buffer;
	return ret;
}

static int quitRomsScan()
{
	DWORD dwExitCode;
	GetExitCodeThread(hScanThread, &dwExitCode);

	if (dwExitCode == STILL_ACTIVE) {
		// Signal the scan thread to abort
		SetEvent(hEvent);
		CloseHandle(hScanThread);
	}

	CloseHandle(hEvent);
	hEvent = NULL;

	hScanThread = NULL;
	scanThreadId = 0;

	romLoader.close();

	rescanRoms = false;

	if (avOk) {
		writeGameAvb();
	}

	return 1;
}

// set scan progress
void romsSetProgress()
{
	SendDlgItemMessage(hRomsDlg, IDC_WAIT_PROG, PBM_STEPIT, 0, 0);
}

void checkScanThread()
{
	// See if we need to abort
	if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) {
		_endthreadex(0);
	}
}

static unsigned __stdcall analyzingRoms(void* param)
{
	int ret = 0; // audit result

	// prepare audit info
	SendDlgItemMessageA(hRomsDlg, IDC_WAIT_LABEL_A, WM_SETTEXT, 0, (LPARAM)"scanning...");
	auditPrepare();
	SendDlgItemMessageA(hRomsDlg, IDC_WAIT_LABEL_A, WM_SETTEXT, 0, (LPARAM)"auditing...");

	unsigned drvCount = BurnGetDrvCount();
	for (unsigned z = 0; z < drvCount; z++) {
		// See if we need to abort
		checkScanThread();

		if (z % 100 == 0) {
			SendDlgItemMessage(hRomsDlg, IDC_WAIT_PROG, PBM_STEPIT, 0, 0);
		}
		ret = auditRomset(z);

		auditState->set(z, ret);
	}

	avOk = true;

	PostMessage(hRomsDlg, WM_CLOSE, 0, 0);

	return 0;
}

static INT_PTR CALLBACK auditWaitProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM)
{
	switch (msg) {
		case WM_INITDIALOG: {
			hRomsDlg = hDlg;

			// ==> load custom resource, added by regret
			HBITMAP hBitmap = (HBITMAP)LoadImage(hAppInst, L"res\\misc.bmp", IMAGE_BITMAP, 80, 60, LR_LOADFROMFILE);
			if (!hBitmap) {
				hBitmap = LoadBitmap(hAppInst, MAKEINTRESOURCE(BMP_MISC));
			}
			SendDlgItemMessage(hDlg, IDC_MISC, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);
			// <== load custom resource

			auditState->reset();

			// set progress bar range
			extern int getFileInfo(bool scanonly);
			romscanMaxRange = getFileInfo(true) + BurnGetDrvCount() / 100;
			SendDlgItemMessage(hDlg, IDC_WAIT_PROG, PBM_SETRANGE, 0, MAKELPARAM(0, romscanMaxRange));
			SendDlgItemMessage(hDlg, IDC_WAIT_PROG, PBM_SETSTEP, (WPARAM)1, 0);

			ShowWindow(GetDlgItem(hDlg, IDC_WAIT_LABEL_A), TRUE);
			ShowWindow(GetDlgItem(hDlg, IDCANCEL), TRUE);

			avOk = false;
			hScanThread = (HANDLE)_beginthreadex(NULL, 0, analyzingRoms, NULL, 0, &scanThreadId);

			hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

			break;
		}

		case WM_COMMAND:
			if (LOWORD(wParam) == IDCANCEL) {
				PostMessage(hDlg, WM_CLOSE, 0, 0);
			}
			break;

		case WM_CLOSE:
			quitRomsScan();
			EndDialog(hDlg, 0);
			hRomsDlg = NULL;
			break;
	}

	return 0;
}

int createROMInfo()
{
	if (!auditState) {
		auditState = new AuditState();
	}
	auditState->init();

	if (checkGameAvb() || rescanRoms) {
		localiseDialogBox(IDD_WAIT, hSelDlg ? hSelDlg : hScrnWnd, (DLGPROC)auditWaitProc);
	}

	return 1;
}
